//
//  CoreDataManager.m
//  CoreDataDemo
//
//  Created by liuxiang on 2018/1/5.
//  Copyright © 2018年 liuxiang. All rights reserved.
//

#import "CoreDataManager.h"
#import "SQLManager.h"
#import "BookModel.h"
#import "BcBook+CoreDataClass.h"
#import "BcItem+CoreDataClass.h"

@interface CDOpearation : NSOperation

@property (strong, nonatomic) NSManagedObjectContext *privatemoc;

@property (strong, nonatomic) NSManagedObjectContext *mainmoc;


@end

@implementation CDOpearation


- (NSManagedObjectContext *) privatemoc {
    if (!_privatemoc) {
        _privatemoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_privatemoc performBlock:^{
            
        }];
    }
    return _privatemoc;
}

- (NSManagedObjectContext *) mainmoc {
    if (!_mainmoc) {
        _mainmoc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    }
    return _mainmoc;
}

@end

@interface CoreDataManager()

@property (strong, nonatomic) id persistentContainer;

//iOS9中 CoreData Stack核心的三个类
//管理模型文件上下文
@property(nonatomic,strong)NSManagedObjectContext *managedObjectContext;
//模型文件
@property(nonatomic,strong)NSManagedObjectModel *managedObjectModel;
//存储调度器
@property(nonatomic,strong)NSPersistentStoreCoordinator *persistentStoreCoordinator;

@property (strong, nonatomic) NSLock *lock;

@end

@implementation CoreDataManager



static CoreDataManager *_sharedCoreDataManager;

+ (instancetype) sharedCoreDataManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedCoreDataManager = [[CoreDataManager alloc] init];
        //        _sharedCoreDataManager.lock = [[NSLock alloc] init];
    });
    return _sharedCoreDataManager;
}

#pragma mark - public interface
- (NSArray *) fectchBooks {
    // 查询
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Bc_books" inManagedObjectContext:self.managedObjectContext];
    [fetchRequest setEntity:entity];
    // Specify criteria for filtering which objects to fetch
    //查询条件
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bookName like%@", @"我"];
    [fetchRequest setPredicate:predicate];
    //查询个数
    fetchRequest.fetchLimit = 10;
    // Specify how the fetched objects should be sorted
    //结果排序
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"bookId"
                                                                   ascending:YES];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];
    
    NSError *error = nil;
    NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    NSLog(@"fetchobjects count = %zi,error = %@",fetchedObjects.count,error.localizedDescription);
    return fetchedObjects;
}

- (BOOL) excuteInsertWithObjects:(NSArray<NSManagedObject *> *)mbs {
  NSLog(@"start insert objs count = %lu",(unsigned long)self.managedObjectContext.insertedObjects.count);
    __weak typeof(self) ws = self;
    [mbs enumerateObjectsUsingBlock:^(NSManagedObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        @autoreleasepool {
            BcBook *b = (BcBook *) obj;
            if (idx == 0) {
                NSLog(@"bookid = %d, bookname = %@",b.bookId,b.bookName);
            }
            //插入之前先查询存不存在，避免重复插入
            NSArray *queryArray = [self excuteQueryWithEntityName:NSStringFromClass(obj.class) condition:[NSString stringWithFormat:@"bookId=%d",b.bookId] limitCount:0 sortKeys:nil ascending:YES];
            //            [self excuteDeleteWithObjects:queryArray];
            for (NSManagedObject *m in queryArray) {
                if (m == obj) {
                    // 查询出来的数据保存已经插入到moc中但是没有保存的数据，如果和obj一样，则是已经插入没有保存的数据，不需要删除，删除的是之前保存的旧数据
                    continue;
                }
                [ws.managedObjectContext deleteObject:m];
//                    NSLog(@"delete objs count = %lu",(unsigned long)self.managedObjectContext.deletedObjects.count);
                NSLog(@"m = %p",m);
            }
//            [self.managedObjectContext insertObject:obj];
            NSLog(@"OBJ = %p",obj);
//            NSLog(@"insert objs count = %lu",(unsigned long)self.managedObjectContext.insertedObjects.count);

        }
    }];
 
    /*
    for (NSManagedObject *mb in mbs) {
      
        @autoreleasepool{
            //再插入的数据，在初始化时已经被插入到moc中，在这里插入不会重复插入
            [self.managedObjectContext insertObject:mb];
        }
    }
     */
      NSLog(@"start insert objs count = %lu",(unsigned long)self.managedObjectContext.insertedObjects.count);
//        NSLog(@"delete objs count = %lu, insert objs count = %lu",(unsigned long)self.managedObjectContext.deletedObjects.count,(unsigned long)self.managedObjectContext.insertedObjects.count);
    return [self saveContext];
}

// 简单查询数据
- (nullable NSArray<NSManagedObject *> *) excuteQueryWithEntityName:(NSString *)entityName condition:(NSString *)predicateString limitCount:(NSUInteger) limitCount sortKeys:(NSArray<NSString *> *)sortKeys ascending:(BOOL)ascending {
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:self.managedObjectContext];
    NSAssert(entity, @"NSEntityDescription nil");
    [fetchRequest setEntity:entity];
    if ([predicateString stringByReplacingOccurrencesOfString:@" " withString:@""].length > 0) {
        // Specify criteria for filtering which objects to fetch
        NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateString];
        [fetchRequest setPredicate:predicate];
    }
    if (limitCount > 0) {
        // Specify fetched fetchLimit
        fetchRequest.fetchLimit = limitCount;
    }
    if (sortKeys.count > 0) {
        // Specify how the fetched objects should be sorted
        NSMutableArray<NSSortDescriptor *> *sortDescriptors = [NSMutableArray arrayWithCapacity:sortKeys.count];
        for (NSString *key in sortKeys) {
            NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:key ascending:ascending];
            [sortDescriptors addObject:sortDescriptor];
        }
        [fetchRequest setSortDescriptors:sortDescriptors];
    }
    
    NSError *error = nil;
    NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
    if (fetchedObjects == nil) {
        NSLog(@"查询失败");
        return nil;
    }
    return fetchedObjects;
}

// 按条件查询删除数据
- (nullable NSDictionary<NSString *, id> *) excuteDeleteWithEntityName:(NSString *)entityName Condition:(NSString *)predicateString {
    NSArray<NSManagedObject *> *fetchResult = [self excuteQueryWithEntityName:entityName condition:predicateString limitCount:0 sortKeys:nil ascending:YES];
    if (fetchResult == nil) {
        //查询失败，直接返回
        return nil;
    }
    for (NSManagedObject *object in fetchResult) {
        [self.managedObjectContext deleteObject:object];
    }
    BOOL result = [self saveContext];
    if (result) {
        return @{MCDeleteResultBoolKey:@(result),
                 MCDeleteManagerObjectsKey:fetchResult
                 };
    }
    return nil;
    
}

/// 删除指定对象
- (BOOL) excuteDeleteWithObjects:(NSArray<NSManagedObject *> *)objs {
    for (NSManagedObject *obj in objs) {
        @autoreleasepool{
            [self.managedObjectContext deleteObject:obj];
        }
    }
    return [self saveContext];
}

// 更新数据 就是保存moc中更改的数据
- (BOOL) excuteUpdateWithObjects {
    if (![self.managedObjectContext hasChanges]) {
        return YES;
    }else {
        return [self.managedObjectContext save:nil];
    }
}

// 从数据库读取数据插入到coreData
- (BOOL) insertDataFromSql {
    NSArray<BookModel *> *books = [[SQLManager sharedSQLManager] findBook];
    NSMutableArray *insterBook = [NSMutableArray arrayWithCapacity:books.count];
    for (BookModel *model in books) {
        @autoreleasepool{
            // 已经被放在插入集合中，可以直接执行保存操作
          BcBook *bcbook = [NSEntityDescription insertNewObjectForEntityForName:@"BcBook" inManagedObjectContext:self.managedObjectContext];
            bcbook.bookId = model.bookId;
            bcbook.bookISBN = model.bookISBN;
            bcbook.bookName = model.bookName;
            bcbook.bookPrice = model.bookPrice;
            bcbook.bookAuthor = model.bookAuthor;
            bcbook.bookNum = model.bookNum;
            bcbook.bookPub = model.bookPub;
            bcbook.bookPubTime = model.bookPubTime;
            bcbook.bookPhoto = model.bookPhoto;
            bcbook.bookDescribe = model.bookDescribe;
            bcbook.bookPage = model.bookPage;
            bcbook.bookSale = model.bookSale;
            bcbook.bookHits = model.bookHits;
            //        bcbook.bookStorageTime = model.bookStorageTime;
            bcbook.bookShowIndex = model.bookShowIndex;
            bcbook.bookOther = model.bookOther;
            bcbook.childTypeId = model.childTypeId;
            [insterBook addObject:bcbook];
        }
    }
    return [self excuteInsertWithObjects:insterBook];
    
    //    return [self saveContext];
}

#pragma mark - getter

- (id)persistentContainer {
    // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
    @synchronized (self) {
        if (_persistentContainer == nil) {
            if (@available(iOS 10.0, *)) {
                _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"CoreDataDemo" managedObjectModel:self.managedObjectModel];
                [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                    if (error != nil) {
                        // Replace this implementation with code to handle the error appropriately.
                        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                        
                        /*
                         Typical reasons for an error here include:
                         * The parent directory does not exist, cannot be created, or disallows writing.
                         * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                         * The device is out of space.
                         * The store could not be migrated to the current model version.
                         Check the error message to determine what the actual problem was.
                         */
                        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    }
                }];
            } else {
                // Fallback on earlier versions
            }
        }
    }
    return _persistentContainer;
}

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {
    
    // Fallback on earlier versions
    if (_persistentStoreCoordinator == nil) {
        _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
        NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:@"demo.db"];
        NSLog(@"dbPath = %@",dbPath);
        NSURL *url = [NSURL fileURLWithPath:dbPath isDirectory:YES];
        [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:nil error:nil];
    }
    return _persistentStoreCoordinator;
}

- (NSManagedObjectModel *) managedObjectModel {
    
    // Fallback on earlier versions
    if (_managedObjectModel == nil) {
        _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    }
    return _managedObjectModel;;
}

- (NSManagedObjectContext *) managedObjectContext {
    
    // Fallback on earlier versions
    if (_managedObjectContext == nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];//NSMainQueueConcurrencyType,NSPrivateQueueConcurrencyType
        _managedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
    }
    return _managedObjectContext;
}

#pragma mark - Core Data Saving support

- (BOOL)saveContext {
    
    NSManagedObjectContext *context = nil;
    
    context = self.managedObjectContext;
    NSError *error = nil;
    NSLog(@"delete objs count = %lu, insert objs count = %lu",(unsigned long)self.managedObjectContext.deletedObjects.count,(unsigned long)self.managedObjectContext.insertedObjects.count);
    
    if ([context hasChanges] && ![context save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        return NO;
    }
    return YES;
}

@end
